Prerrequisitos

Se requieren cargar las siguientes paqueterĆ­as para seguir los ejemplos.

library(easypackages)
libraries("tidyverse","fpp3","plotly", "patchwork")

Introducción

Los modelos que veremos aquƭ tienen como idea principal encontrar relaciones lineales entre la serie que queremos pronosticar, \(y\), con una o mƔs series distintas, x. En otras palabras, pronosticaremos los valores futuros de una serie, a partir de los cambios en otra serie que la afecte.

Es muy común querer predecir de esta forma. Por ejemplo, una tienda de helados podría encontrar una relación entre sus ventas (\(y\)) y la temperatura (\(x_1\)). O las ventas de Nike, a partir de cuÔnto gastan en publicidad y mercadotecnia.

En la literatura podemos encontrar muchos nombres para las variables \(y\) ^ \(x\). P. ej.

\(y\) (var. de pronóstico) \(x\) (vars. predictoras)
Var. dependiente Vars. independientes
Explicada Explicativas
Regresada Regresoras
Respuesta EstĆ­mulo
Resultado Covariante
Controlada De control

El modelo lineal

El caso mÔs sencillo sería un modelo de regresión lineal simple, de la forma:

\[ y_t = \beta_0 + \beta_1 x_t + \varepsilon_t \]

donde

  • \(\beta_0\) es conocido como el intercepto y representa el valor predicho cuando \(x = 0\).

  • \(\beta_1\) es la pendiente de la recta. Nos indica el cambio promedio en \(y\), ante un cambio en una unidad de \(x\).

  • El tĆ©rmino de error, \(\varepsilon_t\) se asume aleatorio y decimos que captura los cambios debido a todas las otras variables que pudieran llegar a afectar a \(y_t\), que no estĆ”n explĆ­citamente especificadas en el modelo.

La recta resultante estƔ dada entonces por \(\beta_0 + \beta_1 x_t\), y la diferencia que existe en los puntos reales y Ʃsta es \(\varepsilon_t\).

Ejemplo: gasto de consumo en EEUU

Como primer ejemplo, veamos las tasas de crecimiento del gasto de consumo, \(y\), y su relación con el ingreso personal disponible, \(x\).

La grƔfica de tiempo de ambas series:

us_change %>%
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Consumption, colour = "Consumo")) +
  geom_line(aes(y = Income, colour = "Ingreso")) +
  ylab("cambio %") + xlab("AƱo") +
  guides(colour=guide_legend(title="Series")) + 
  theme(legend.position = "top")

Un diagrama de dispersión entre ambas series, para ver una posible correlación.

us_change %>%
  ggplot(aes(x=Income, y=Consumption)) +
    ylab("Consumo (cambio % trimestral)") +
    xlab("Ingreso (cambio % trimestral)") +
    geom_point() +
    geom_smooth(method="lm", se=FALSE)

La línea azul en el grÔfico sigue la ecuación que describe la regresión lineal que tiene por variable dependiente al consumo y como independiente al ingreso.

¿Qué es una regresión lineal?

El tĆ©rmino de regresión fue acuƱado por primera vez por Francis Galton en 1886. Ɖl estaba estudiando la relación que existe entre la estatura de los hijos y la estatura de los padres.

Lo que encontró fue lo siguiente, en resumen:

  • Los padres mĆ”s altos, tendĆ­an a tener hijos mĆ”s altos, mientras que los padres bajos tendĆ­an a tener hijos bajos.

  • En promedio, los hijos de padres altos no logran ser mĆ”s altos que ellos. Similarmente, los hijos de padres bajos, en promedio son mĆ”s altos que sus papĆ”s.

  • AsĆ­, Galton decĆ­a que habĆ­a una tendencia a regresar a la estatura promedio.

De no cumplirse la regresión de Galton, sería común tener gente de la estatura de un Hobbit, y también de la estatura de un gigante.

Entonces, el anÔlisis de regresión en tiempos modernos trata sobre la relación de la dependencia entre una variable \(y\), respecto de una o mÔs variables exógenas (regresoras \(x\)) para predecir el valor promedio de la variable dependiente.

Regresión y causalidad

ā€œUna relación estadĆ­stica, por mĆ”s fuerte y sugerente que sea, nunca podrĆ” establecer una conexión causal: nuestras ideas de causalidad deben provenir de estadĆ­sticas externas y, en Ćŗltimo tĆ©rmino, de una u otra teorĆ­aā€ (Kendall & Stuart, 1961)

Regresión \(\neq\) Causalidad

Correlación \(\neq\) Causalidad



  • Una relación estadĆ­stica por sĆ­ misma no puede implicar causalidad.
    • Se debe acudir a consideraciones a priori o teóricas.


  • La causalidad puede determinarse tambiĆ©n por sentido comĆŗn.

¿Qué significa que un modelo sea lineal?

Se puede hablar de linealidad en dos sentidos:

  1. Linealidad en las variables; \(x\).
  • La esperanza condicional de \(y\) es una función lineal de \(x_i\).
  1. Linealidad en los parƔmetros; \(\beta\).
  • La esperanza condicional de \(y\) es lineal en los parĆ”metros \(\beta_i\).

Para un modelo de regresión lineal, solo nos interesa que sea lineal en los parÔmetros.

Todas estas funciones son lineales en los parÔmetros y pueden ser estimadas mediante un modelo de regresión lineal

Así, un modelo de regresión lineal puede generar una recta, o una variedad de curvas, dependiendo la forma funcional que se elija.

MĆ­nimos cuadrados ordinarios (MCO-OLS)

\[ H_0: \beta_0 = 0 \\ H_1: \beta_0 \neq 0 \] \[ H_0: \beta_1 = 0 \\ H_1: \beta_1 \neq 0 \]

\[ \hat{y}_t = \hat{\beta}_0 + \hat{\beta}_1x_t + \hat{\varepsilon}_t \\ \hat{y}_t = 0.54454 + 0.27183 x_t + \hat{\varepsilon}_t \]

\[ y_{consumo} = \beta_0 + \beta_1 x_{income} + \beta_2 x_{production} + \beta_3 x_{savings} + \beta_4 x_{unemployment} \]

Ejemplos

US % change

us_change
us_change %>% 
  as_tibble() %>% 
  select(-Quarter) %>% 
  GGally::ggpairs()

Una correlación, por mÔs fuerte que sea entre dos variables, no puede implicar por sí misma causalidad.

us_change %>% 
  pivot_longer(cols = -Quarter) %>% 
  ggplot(aes(x = Quarter, y = value, color = name)) +
  geom_line() +
  facet_wrap(~ name, scales = "free_y") +
  theme(legend.position = "none")

Graficando nuestra variable de pronóstico (Consumo) vs. cada una de las variables predictoras:

us_change %>% 
  pivot_longer(cols = -c(Quarter, Consumption)) %>% 
  ggplot(aes(x = Quarter, y = value, color = name)) +
  geom_line() +
  geom_line(aes(y = Consumption), color = "black") +
  facet_wrap(~ name, scales = "free_y") +
  theme(legend.position = "none")

Regresión lineal simple

Realizamos un primer modelo, donde utilizaremos de variable predictora al ingreso disponible, para pronosticar el consumo.

\[ y_{t,Consumo} = \beta_0 + \beta_{1}x_{t,Ingreso} + \varepsilon_t \]

fit1 <- us_change %>% 
  model(reg_lin_simple = TSLM(Consumption ~ Income)
        )
fit1 %>%  report()
Series: Consumption 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-2.58236 -0.27777  0.01862  0.32330  1.42229 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.54454    0.05403  10.079  < 2e-16 ***
Income       0.27183    0.04673   5.817  2.4e-08 ***
---
Signif. codes:  
0 ā€˜***’ 0.001 ā€˜**’ 0.01 ā€˜*’ 0.05 ā€˜.’ 0.1 ā€˜ ’ 1

Residual standard error: 0.5905 on 196 degrees of freedom
Multiple R-squared: 0.1472, Adjusted R-squared: 0.1429
F-statistic: 33.84 on 1 and 196 DF, p-value: 2.4022e-08

Prueba de significancia individual para cada uno de los parƔmetros (\(\beta_i\))

\[ H_0: \beta_i = 0 \]

Prueba de significancia conjunta (para revisar si nuestro modelo sirve o no)

\[ H_0: \beta_1 = \beta_2 = \beta_3 = \ldots = 0 \]

augment(fit1) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Consumption, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted"))+
  xlab("AƱo") + ylab(NULL) +
  ggtitle("Cambios porcentuales en el gasto de Consumo en EEUU") +
  guides(color = guide_legend(title = NULL))

El modelo no parece capturar adecuadamente la variación de los datos reales.

augment(fit1) %>% 
  ggplot(aes(x = Consumption, y = .fitted)) +
  geom_point() +
  ylab("Fitted (valores ajustados)") +
  xlab("Datos (reales históricos)") +
  ggtitle("Cambios porcentuales en el gasto de Consumo en EEUU") +
  geom_abline(intercept = 0, slope = 1)

fit1 %>% 
  gg_tsresiduals()

augment(fit1) %>% 
  features(.resid, ljung_box, lag= 10, dof = 2)

Es evidente que este modelo se puede mejorar. Probemos incluyendo las otras predictoras.

Regresión lineal múltiple

Podríamos proponer ahora un modelo en donde el consumo sea una función del ingreso, la producción, los ahorros y el desempleo:

\[ y_{t,Consumo} = \beta_0 + \beta_{1}x_{t,Ingreso} + \beta_2x_{t,Producción} + \beta_3x_{t,Ahorros} + \beta_4x_{t,Desempleo} + \varepsilon_t \]

fit2 <- us_change %>% 
  model(
    reg_lin_multiple = TSLM(Consumption ~ Income + Production + Savings + Unemployment)
  )
report(fit2)
Series: Consumption 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.90555 -0.15821 -0.03608  0.13618  1.15471 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   0.253105   0.034470   7.343 5.71e-12 ***
Income        0.740583   0.040115  18.461  < 2e-16 ***
Production    0.047173   0.023142   2.038   0.0429 *  
Savings      -0.052890   0.002924 -18.088  < 2e-16 ***
Unemployment -0.174685   0.095511  -1.829   0.0689 .  
---
Signif. codes:  
0 ā€˜***’ 0.001 ā€˜**’ 0.01 ā€˜*’ 0.05 ā€˜.’ 0.1 ā€˜ ’ 1

Residual standard error: 0.3102 on 193 degrees of freedom
Multiple R-squared: 0.7683, Adjusted R-squared: 0.7635
F-statistic:   160 on 4 and 193 DF, p-value: < 2.22e-16
augment(fit2) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Consumption, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted"))+
  xlab("AƱo") + ylab(NULL) +
  ggtitle("Cambios porcentuales en el gasto de Consumo en EEUU") +
  guides(color = guide_legend(title = NULL))

Este modelo parece capturar mÔs variación de los datos históricos.

augment(fit2) %>% 
  ggplot(aes(x = Consumption, y = .fitted)) +
  geom_point() +
  ylab("Fitted (valores ajustados)") +
  xlab("Datos (reales históricos)") +
  ggtitle("Cambios porcentuales en el gasto de Consumo en EEUU") +
  geom_abline(intercept = 0, slope = 1)

fit2 %>% 
  gg_tsresiduals()

augment(fit2) %>% 
  features(.resid, ljung_box, lag= 10, dof = 2)
df <- left_join(us_change, residuals(fit2), by = "Quarter")
df %>% 
  select(-c(Consumption, .model)) %>% 
  pivot_longer(cols = c(Income:Unemployment)) %>% 
  ggplot(aes( x = value, y = .resid, color = name)) + 
  geom_point() + ylab("Residuales") + xlab("Predictoras") +
  facet_wrap(~ name, scales = "free_x") +
  theme(legend.position = "none")

augment(fit2) %>% 
  ggplot(aes(x = .fitted, y = .resid)) +
  geom_point() +
  labs(x = "Ajustados", y = "Residuales")

glance(fit2) %>% 
  select(adj_r_squared, AIC, AICc, BIC)

Selección de predictoras

1. Escoger subconjuntos de predictoras y probarlo.

fit3 <- us_change %>% 
  model(r1 = TSLM(Consumption ~ Income),
        r2 = TSLM(Consumption ~ Income + Production),
        r3 = TSLM(Consumption ~ Income + Production + Savings + Unemployment),
        r4 = TSLM(Consumption ~ Income + Production + Savings),
        r5 = TSLM(Consumption ~ Income + Savings + Unemployment),
        r6 = TSLM(Consumption ~ Income + Production + Unemployment),
        r7 = TSLM(Consumption ~ Income + Savings)
        )
fit3 %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)
fit3 %>% 
  select(r3) %>% 
  report()
Series: Consumption 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.90555 -0.15821 -0.03608  0.13618  1.15471 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   0.253105   0.034470   7.343 5.71e-12 ***
Income        0.740583   0.040115  18.461  < 2e-16 ***
Production    0.047173   0.023142   2.038   0.0429 *  
Savings      -0.052890   0.002924 -18.088  < 2e-16 ***
Unemployment -0.174685   0.095511  -1.829   0.0689 .  
---
Signif. codes:  
0 ā€˜***’ 0.001 ā€˜**’ 0.01 ā€˜*’ 0.05 ā€˜.’ 0.1 ā€˜ ’ 1

Residual standard error: 0.3102 on 193 degrees of freedom
Multiple R-squared: 0.7683, Adjusted R-squared: 0.7635
F-statistic:   160 on 4 and 193 DF, p-value: < 2.22e-16

2. Backwards stepwise regression:

  • Empezamos con un modelo que contenga todas las predictoras.
  • Quitamos una a la vez.
  • Mantenemos el modelo si mejora la medida de desempeƱo predictivo (\(\bar{R}^2\), AICc,…).
  • Seguirlo haciendo hasta no encontrar mejoras adicionales.

Selección del modelo a través de backwards stepwise regression con base en el \(AICc\) sugiere conservar el modelo que incluye todas las predictoras.

us_change %>% 
  model(i = TSLM(Consumption ~ Income + Production + Savings + Unemployment),
        ii = TSLM(Consumption ~ Income + Production + Savings),
        iii = TSLM(Consumption ~ Income + Production + Unemployment),
        iv = TSLM(Consumption ~ Income + Savings + Unemployment),
        v = TSLM(Consumption ~ Production + Savings + Unemployment)
        ) %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)

Con base en \(BIC\) parece que el mejor modelo es el que incluye todas las predictoras menos el desempleo.

us_change %>% 
  model(i = TSLM(Consumption ~ Income + Production + Savings + Unemployment),
        ii = TSLM(Consumption ~ Income + Production + Savings),
        iii = TSLM(Consumption ~ Income + Production),
        iv = TSLM(Consumption ~ Income + Savings),
        v = TSLM(Consumption ~ Production + Savings),
        vi = TSLM(Consumption ~ Income + Savings + Unemployment),
        vii = TSLM(Consumption ~ Production + Savings + Unemployment),
        viii = TSLM(Consumption ~ Income + Production + Unemployment)
        ) %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)

3. Forwards stepwise regression:

  • Comenzar con un modelo que solo incluya al intercepto.
  • Se van agregando las predictoras una a la vez.
  • La predictora que mejore mĆ”s al modelo se mantiene.
  • Se itera hasta no tener mejorĆ­a adicional.
us_change %>% 
  model(i = TSLM(Consumption ~ Income),
        ii = TSLM(Consumption ~ Production),
        iii = TSLM(Consumption ~ Savings),
        iv = TSLM(Consumption ~ Unemployment)
        ) %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)

El mejor modelo hasta ahora es el que toma de predictora a la producción.

us_change %>% 
  model(
    o   = TSLM(Consumption ~ Production),
    i   = TSLM(Consumption ~ Production + Income),
    ii  = TSLM(Consumption ~ Production + Savings),
    iii = TSLM(Consumption ~ Production + Unemployment)
  ) %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)

El mejor hasta ahora es el modelo con la producción y el ingreso.

us_change %>% 
  model(
    i   = TSLM(Consumption ~ Production + Income),
    ii  = TSLM(Consumption ~ Production + Income + Savings),
    iii = TSLM(Consumption ~ Production + Income + Unemployment)
  ) %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)

El modelo mejora agregando los ahorros tambiƩn.

us_change %>% 
  model(
    i   = TSLM(Consumption ~ Production + Income + Savings),
    ii  = TSLM(Consumption ~ Production + Income + Savings + Unemployment)
  ) %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)

Utilizando la adj_r_squared o el AICc, concluimos lo mismo que antes: el modelo que incluye a todas las variables es el mejor. ### {-}

Pronóstico

Pronósticos ex-ante

En estos pronósticos solo se utiliza información disponible hasta el último dato del histórico. A estos pronósticos se les considera como pronósticos reales. Aquí las predictoras se deben pronosticar antes de poder producir el pronóstico de la variable de interés.

Pronósticos ex-post

Con estos pronósticos se utiliza información real disponible de las predictoras. Estos pronósticos ya no son reales (en el sentido estricto). La variable a pronosticar (\(y\)) sigue siendo desconocida.

Pronósticos basados en escenarios

fit_escenarios <- us_change %>% 
  model(lineal = TSLM(Consumption ~ Income + Savings + Unemployment))

# Necesitamos agregar nuevos datos de las predictoras
escenarios <- scenarios(
  optimista = new_data(us_change, 4) %>% 
    mutate(Income = c(0,0.2,0,1.2), Savings = c(0,-0.1,0.5,-1), Unemployment = -0.1),
  pesimista = new_data(us_change, 4) %>% 
    mutate(Income = -1, Savings = -0.5, Unemployment = 0.1),
  names_to = "Escenario"
)

fc_escenarios <- fit_escenarios %>% 
  forecast(new_data = escenarios)

us_change %>% 
  autoplot(Consumption) +
  autolayer(fc_escenarios)

Vamos a generar un pronóstico ex-ante del consumo. Para esto, necesitamos primero producir pronósticos de las predictoras:

# mod_predictoras <- function(predictora, horizonte = 4) {
#   us_change %>% 
#     model(predictora = ARIMA(as.formula(predictora)) %>% 
#     forecast(h = horizonte)
# }
# 
# mod_predictoras(predictora = Income)

ingreso <-  us_change %>% 
  model(ETS = ETS(Income),
        ARIMA = ARIMA(Income)
        ) %>% 
  forecast(h = 4) 

ingreso %>% 
  autoplot(us_change, level = NULL)


produccion <- us_change %>% 
  model(
    ETS = ETS(Production),
    ARIMA = ARIMA(Production)
  ) %>% 
  forecast(h = 4)
produccion %>% 
  autoplot(us_change, level = NULL)


ahorro <- us_change %>% 
  model(
    ETS = ETS(Savings),
    ARIMA = ARIMA(Savings)
  ) %>% 
  forecast(h = 4)
ahorro %>% 
  autoplot(us_change, level = NULL)


desempleo <- us_change %>% 
  model(
    ETS = ETS(Unemployment),
    ARIMA = ARIMA(Unemployment)
  ) %>% 
  forecast(h = 4)
desempleo %>% 
  autoplot(us_change, level = NULL)

Teniendo ya los pronósticos de cada predictora, podemos proceder a generar el pronóstico del consumo.

fit <- us_change %>% 
  model(
    `Regresión lineal múltiple` = TSLM(Consumption ~ Income + Production + Savings + Unemployment)
  )

datos_futuros <- new_data(us_change,4) %>% 
  mutate(Income = ingreso %>% filter(.model == "ARIMA") %>% pull(.mean), 
         Savings = ahorro %>% filter(.model == "ARIMA") %>% pull(.mean), 
         Unemployment = desempleo %>% filter(.model == "ARIMA") %>% pull(.mean),
         Production = produccion %>% filter(.model == "ARIMA") %>% pull(.mean))

datos_futuros

fc <- forecast(fit, datos_futuros)

fc %>% 
  autoplot(us_change)

fc %>% 
  autoplot(us_change %>% filter_index("2016 Q1" ~ .))

Inclusión de predictoras útiles: Producción de cerveza

recent_production <- aus_production %>% 
  filter(year(Quarter) >= 1992)

recent_production

recent_production %>% 
  autoplot(Beer) +
  labs(x = "AƱo", y = "Megalitros", 
       title = "Producción de cerveza trimestral en Australia")

Existen varias predictoras que pueden ser útiles en el anÔlisis de regresión.

  1. Predictora de tendencia trend()

\[ y_t = \beta_0 + \beta_1t + \varepsilon_t \]

  1. Variables dummy estacionales

Las variables dummy (tambiĆ©n llamadas dicotómicas, binarias, …) toman solo dos valores: 1 o 0 (verdadero/falso).

Para agregar variables dummy estacionales, basta con escribir season().

recent_production %>% 
  select(Quarter,Beer) %>% 
  mutate(tendencia = seq_along(recent_production$Quarter),
         q2 = if_else(quarter(Quarter)==2,1,0),
         q3 = if_else(quarter(Quarter)==3,1,0),
         q4 = if_else(quarter(Quarter)==4,1,0)
         ) %>% 
  model(TSLM(Beer ~ tendencia + q2 + q3 + q4)) %>% 
  report()
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-42.9029  -7.5995  -0.4594   7.9908  21.7895 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 441.80044    3.73353 118.333  < 2e-16 ***
tendencia    -0.34027    0.06657  -5.111 2.73e-06 ***
q2          -34.65973    3.96832  -8.734 9.10e-13 ***
q3          -17.82164    4.02249  -4.430 3.45e-05 ***
q4           72.79641    4.02305  18.095  < 2e-16 ***
---
Signif. codes:  
0 ā€˜***’ 0.001 ā€˜**’ 0.01 ā€˜*’ 0.05 ā€˜.’ 0.1 ā€˜ ’ 1

Residual standard error: 12.23 on 69 degrees of freedom
Multiple R-squared: 0.9243, Adjusted R-squared: 0.9199
F-statistic: 210.7 on 4 and 69 DF, p-value: < 2.22e-16
fit_beer <- recent_production %>% 
  model(TSLM(Beer ~ trend() + season()))

report(fit_beer)
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-42.9029  -7.5995  -0.4594   7.9908  21.7895 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)
(Intercept)   441.80044    3.73353 118.333  < 2e-16
trend()        -0.34027    0.06657  -5.111 2.73e-06
season()year2 -34.65973    3.96832  -8.734 9.10e-13
season()year3 -17.82164    4.02249  -4.430 3.45e-05
season()year4  72.79641    4.02305  18.095  < 2e-16
                 
(Intercept)   ***
trend()       ***
season()year2 ***
season()year3 ***
season()year4 ***
---
Signif. codes:  
0 ā€˜***’ 0.001 ā€˜**’ 0.01 ā€˜*’ 0.05 ā€˜.’ 0.1 ā€˜ ’ 1

Residual standard error: 12.23 on 69 degrees of freedom
Multiple R-squared: 0.9243, Adjusted R-squared: 0.9199
F-statistic: 210.7 on 4 and 69 DF, p-value: < 2.22e-16
p <- augment(fit_beer) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Beer, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted")) +
  labs(x = "AƱo", y = "Megalitros")

ggplotly(p)

Al ver la grÔfica, vemos que existe un trimestre muy por debajo del resto. Podemos agregar una variable de intervención para ese periodo.

Spike variables

Capturan el efecto de un solo periodo.

cerveza <- recent_production %>% 
  select(Quarter, Beer) %>% 
  mutate(
    q2_94 = if_else(Quarter == yearquarter("1994 Q2"),1,0),
    q4_04 = if_else(Quarter == yearquarter("2004 Q4"),1,0)
  )
cerveza

Ajustamos un modelo, corrigiendo por ese periodo outlier (1994 Q2).

fit_beer <- cerveza %>% 
  model(TSLM(Beer ~ trend() + season() + q2_94 + q4_04))

report(fit_beer)
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-27.0379  -5.8811  -0.6895   7.7209  21.7895 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)
(Intercept)   441.84123    3.32189 133.009  < 2e-16
trend()        -0.34137    0.05975  -5.713 2.78e-07
season()year2 -33.39369    3.55788  -9.386 7.83e-14
season()year3 -17.82164    3.55460  -5.014 4.16e-06
season()year4  75.32030    3.60790  20.876  < 2e-16
q2_94         -24.03384   11.24265  -2.138 0.036190
q4_04         -45.41027   11.15547  -4.071 0.000126
                 
(Intercept)   ***
trend()       ***
season()year2 ***
season()year3 ***
season()year4 ***
q2_94         *  
q4_04         ***
---
Signif. codes:  
0 ā€˜***’ 0.001 ā€˜**’ 0.01 ā€˜*’ 0.05 ā€˜.’ 0.1 ā€˜ ’ 1

Residual standard error: 10.81 on 67 degrees of freedom
Multiple R-squared: 0.9426, Adjusted R-squared: 0.9375
F-statistic: 183.4 on 6 and 67 DF, p-value: < 2.22e-16
p <- augment(fit_beer) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Beer, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted")) +
  labs(x = "AƱo", y = "Megalitros")

ggplotly(p)

Cambios de nivel

Capturan el efecto a partir de cierto periodo.

cerveza <- cerveza %>% 
  mutate(d2000 = if_else(year(Quarter)>=2000,1,0))
cerveza
fit_beer <- cerveza %>% 
  model(TSLM(Beer ~ trend() + season() + d2000))

report(fit_beer)
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-43.0235  -7.5945  -0.4539   7.7754  21.4829 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   441.9154     3.8644 114.354  < 2e-16 ***
trend()        -0.3548     0.1308  -2.712  0.00846 ** 
season()year2 -34.6452     3.9985  -8.665 1.36e-12 ***
season()year3 -17.8046     4.0536  -4.392 4.02e-05 ***
season()year4  72.8279     4.0594  17.941  < 2e-16 ***
d2000           0.7282     5.6402   0.129  0.89766    
---
Signif. codes:  
0 ā€˜***’ 0.001 ā€˜**’ 0.01 ā€˜*’ 0.05 ā€˜.’ 0.1 ā€˜ ’ 1

Residual standard error: 12.32 on 68 degrees of freedom
Multiple R-squared: 0.9243, Adjusted R-squared: 0.9188
F-statistic: 166.1 on 5 and 68 DF, p-value: < 2.22e-16
p <- augment(fit_beer) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Beer, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted")) +
  labs(x = "AƱo", y = "Megalitros")

ggplotly(p)
cerveza %>% 
  gg_tsdisplay(Beer %>% difference(4), plot_type = "partial")

tibble(mujer = c(1,0,1, 1),
       hombre = c(0,1,0, 0),
       nombre = c("Andrea","Juan","SofĆ­a","Fer"))

Cuando se crean varaibles dummy, la cantidad de dummies a generar es una menos que el total de categorĆ­as. Si son dos categorĆ­as, solo necesitamos una dummy. Si son tres, necesitamos 2, etc.

Regresiones no lineales

Modelos exponenciales

  • Modelos log-log

\[ \log{y_t} = \beta_0 + \beta_1 \log{x_t} \]

La interpretación de los coeficientes (\(\beta_s\)) es como elasticidades (cambios porcentuales).

  • Modelos lin-log

\[ y_t = \beta_0 + \beta_1 \log{x_t} \]

  • Modelos log-lin

\[ \log{y_t} = \beta_0 + \beta_1 x_t \]

Modelos de regresión lineal por partes (piecewise)

Aquí la regresión se calcula por partes en el tiempo, para capturar distintas tendencias.

boston_men <- boston_marathon %>% 
  filter(Event == "Men's open division") %>% 
  mutate(Minutes = as.numeric(Time)/60)

p <- boston_men %>% 
  autoplot(Minutes) + 
  ggtitle("Tiempos ganadores del maratón de Boston, categoría abierta de hombres")

ggplotly(p)
fit_boston <- boston_men %>% 
  model(
    lineal = TSLM(Minutes ~ trend()),
    )

fc_boston <- fit_boston %>% forecast(h = 10)

boston_men %>% 
  autoplot(Minutes) +
  geom_line(aes(y = .fitted, color = .model), data = fitted(fit_boston)) +
  autolayer(fc_boston, alpha = 0.5, level = 95) +
  ggtitle("Maratón de Boston, cat. abierta de hombres")

Vamos a modelar los tiempos con una regresión por partes.

fit_boston <- boston_men %>% 
  model(
    lineal = TSLM(Minutes ~ trend()),
    exponencial = TSLM(log(Minutes) ~ trend()),
    `Reg. por partes` = TSLM(Minutes ~ trend(knots = c(1940,1980)))
  )

fc_boston <- fit_boston %>% forecast(h = 10)

boston_men %>% 
  autoplot(Minutes) +
  geom_line(aes(y = .fitted, color = .model), data = fitted(fit_boston)) +
  autolayer(fc_boston, alpha = 0.5, level = 95) +
  ggtitle("Maratón de Boston, cat. abierta de hombres")

Intentamos ahora poniendo los cambios en la tendencia en otros periodos:

fit_boston <- boston_men %>% 
  model(
    lineal = TSLM(Minutes ~ trend()),
    exponencial = TSLM(log(Minutes) ~ trend()),
    `Reg. por partes` = TSLM(Minutes ~ trend(knots = c(1940,1980))),
    `Reg. por partes2` = TSLM(Minutes ~ trend(knots = c(1975))),
    `Reg. por partes3` = TSLM(Minutes ~ trend(knots = c(1915, 1950, 1988))),
    `Reg. por partes4` = TSLM(Minutes ~ trend(knots = c(1915, 1927, 1950, 1985)))
  )

fc_boston <- fit_boston %>% forecast(h = 10)

boston_men %>% 
  autoplot(Minutes) +
  geom_line(aes(y = .fitted, color = .model), data = fitted(fit_boston)) +
  autolayer(fc_boston, alpha = 0.5, level = 95) +
  ggtitle("Maratón de Boston, cat. abierta de hombres")

accuracy(fit_boston) %>% 
  arrange(MAPE)

Probamos transformando la serie con Box-Cox:

lambda <- boston_men %>% 
  features(Minutes, guerrero) %>% 
  pull(lambda_guerrero)

p1 <- boston_men %>% 
  autoplot(Minutes)

p2 <- boston_men %>% 
  autoplot(box_cox(Minutes, lambda = lambda))

p3 <- boston_men %>% 
  autoplot(log(Minutes))

p1/p2/p3

LS0tDQp0aXRsZTogIk1vZGVsb3MgZGUgcmVncmVzacOzbiBwYXJhIHNlcmllcyBkZSB0aWVtcG8iDQphdXRob3I6ICJQYWJsbyBCZW5hdmlkZXMtSGVycmVyYSINCmRhdGU6IDIwMjAtMTItMDYNCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgdGhlbWU6IHVuaXRlZA0KICAgIGhpZ2hsaWdodDogdGFuZ28NCi0tLQ0KDQojIFByZXJyZXF1aXNpdG9zDQoNClNlIHJlcXVpZXJlbiBjYXJnYXIgbGFzIHNpZ3VpZW50ZXMgcGFxdWV0ZXLDrWFzIHBhcmEgc2VndWlyIGxvcyBlamVtcGxvcy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGVhc3lwYWNrYWdlcykNCmxpYnJhcmllcygidGlkeXZlcnNlIiwiZnBwMyIsInBsb3RseSIsICJwYXRjaHdvcmsiKQ0KYGBgDQoNCg0KIyBJbnRyb2R1Y2Npw7NuDQoNCkxvcyBtb2RlbG9zIHF1ZSB2ZXJlbW9zIGFxdcOtIHRpZW5lbiBjb21vIGlkZWEgcHJpbmNpcGFsIGVuY29udHJhciByZWxhY2lvbmVzIGxpbmVhbGVzIGVudHJlIGxhIHNlcmllIHF1ZSBxdWVyZW1vcyBwcm9ub3N0aWNhciwgJHkkLCBjb24gdW5hIG8gbcOhcyBzZXJpZXMgZGlzdGludGFzLCAqeCouIEVuIG90cmFzIHBhbGFicmFzLCBwcm9ub3N0aWNhcmVtb3MgbG9zIHZhbG9yZXMgZnV0dXJvcyBkZSB1bmEgc2VyaWUsIGEgcGFydGlyIGRlIGxvcyBjYW1iaW9zIGVuIG90cmEgc2VyaWUgcXVlIGxhIGFmZWN0ZS4NCg0KRXMgbXV5IGNvbcO6biBxdWVyZXIgcHJlZGVjaXIgZGUgZXN0YSBmb3JtYS4gUG9yIGVqZW1wbG8sIHVuYSB0aWVuZGEgZGUgaGVsYWRvcyBwb2Ryw61hIGVuY29udHJhciB1bmEgcmVsYWNpw7NuIGVudHJlIHN1cyAqKnZlbnRhcyoqICgkeSQpIHkgbGEgKip0ZW1wZXJhdHVyYSoqICgkeF8xJCkuIE8gbGFzICoqdmVudGFzKiogZGUgKk5pa2UqLCBhIHBhcnRpciBkZSBjdcOhbnRvICoqZ2FzdGFuIGVuIHB1YmxpY2lkYWQgeSBtZXJjYWRvdGVjbmlhKiouDQoNCkVuIGxhIGxpdGVyYXR1cmEgcG9kZW1vcyBlbmNvbnRyYXIgbXVjaG9zIG5vbWJyZXMgcGFyYSBsYXMgdmFyaWFibGVzICR5JCBeICR4JC4gUC4gZWouDQoNCnwgJHkkICh2YXIuIGRlIHByb27Ds3N0aWNvKSB8ICR4JCAodmFycy4gcHJlZGljdG9yYXMpIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06fA0KfCBWYXIuIGRlcGVuZGllbnRlICAgICAgICAgfCAgVmFycy4gaW5kZXBlbmRpZW50ZXMgICB8DQp8IEV4cGxpY2FkYSAgICAgICAgICAgICAgICB8ICBFeHBsaWNhdGl2YXMgICAgICAgICAgIHwNCnwgUmVncmVzYWRhICAgICAgICAgICAgICAgIHwgIFJlZ3Jlc29yYXMgICAgICAgICAgICAgfA0KfCBSZXNwdWVzdGEgICAgICAgICAgICAgICAgfCAgRXN0w61tdWxvICAgICAgICAgICAgICAgfA0KfCBSZXN1bHRhZG8gICAgICAgICAgICAgICAgfCAgQ292YXJpYW50ZSAgICAgICAgICAgICB8DQp8IENvbnRyb2xhZGEgICAgICAgICAgICAgICB8ICBEZSBjb250cm9sICAgICAgICAgICAgIHwNCg0KDQojIEVsIG1vZGVsbyBsaW5lYWwNCg0KRWwgY2FzbyBtw6FzIHNlbmNpbGxvIHNlcsOtYSB1biAqKm1vZGVsbyBkZSByZWdyZXNpw7NuIGxpbmVhbCBzaW1wbGUqKiwgZGUgbGEgZm9ybWE6DQoNCiQkDQp5X3QgPSBcYmV0YV8wICsgXGJldGFfMSB4X3QgKyBcdmFyZXBzaWxvbl90DQokJA0KDQpkb25kZSANCg0KKiAkXGJldGFfMCQgZXMgY29ub2NpZG8gY29tbyBlbCAqaW50ZXJjZXB0byogeSByZXByZXNlbnRhIGVsICoqdmFsb3IgcHJlZGljaG8gY3VhbmRvICR4ID0gMCQuKiogDQoNCiogJFxiZXRhXzEkIGVzIGxhICpwZW5kaWVudGUqIGRlIGxhIHJlY3RhLiBOb3MgaW5kaWNhIGVsICoqY2FtYmlvIHByb21lZGlvIGVuICR5JCwgYW50ZSB1biBjYW1iaW8gZW4gdW5hIHVuaWRhZCBkZSAkeCQqKi4NCg0KKiBFbCB0w6lybWlubyBkZSBlcnJvciwgJFx2YXJlcHNpbG9uX3QkIHNlIGFzdW1lIGFsZWF0b3JpbyB5IGRlY2ltb3MgcXVlIGNhcHR1cmEgbG9zIGNhbWJpb3MgZGViaWRvIGEgdG9kYXMgbGFzIG90cmFzIHZhcmlhYmxlcyBxdWUgcHVkaWVyYW4gbGxlZ2FyIGEgYWZlY3RhciBhICR5X3QkLCBxdWUgbm8gZXN0w6FuIGV4cGzDrWNpdGFtZW50ZSBlc3BlY2lmaWNhZGFzIGVuIGVsIG1vZGVsby4NCg0KTGEgcmVjdGEgcmVzdWx0YW50ZSBlc3TDoSBkYWRhIGVudG9uY2VzIHBvciAkXGJldGFfMCArIFxiZXRhXzEgeF90JCwgeSBsYSBkaWZlcmVuY2lhIHF1ZSBleGlzdGUgZW4gbG9zIHB1bnRvcyByZWFsZXMgeSDDqXN0YSBlcyAkXHZhcmVwc2lsb25fdCQuDQoNCg0KIVsqWyhIeW5kbWFuLCAyMDE5KV0oaHR0cHM6Ly9vdGV4dHMuY29tL2ZwcDMvKSpdKC4uL2ltYWdlcy9saW5yZWcucG5nKQ0KDQojIyBFamVtcGxvOiBnYXN0byBkZSBjb25zdW1vIGVuIEVFVVUNCg0KQ29tbyBwcmltZXIgZWplbXBsbywgdmVhbW9zIGxhcyB0YXNhcyBkZSBjcmVjaW1pZW50byBkZWwgZ2FzdG8gZGUgY29uc3VtbywgJHkkLCB5IHN1IHJlbGFjacOzbiBjb24gZWwgaW5ncmVzbyBwZXJzb25hbCBkaXNwb25pYmxlLCAkeCQuDQoNCkxhIGdyw6FmaWNhIGRlIHRpZW1wbyBkZSBhbWJhcyBzZXJpZXM6DQoNCmBgYHtyfQ0KdXNfY2hhbmdlICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBRdWFydGVyKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBDb25zdW1wdGlvbiwgY29sb3VyID0gIkNvbnN1bW8iKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBJbmNvbWUsIGNvbG91ciA9ICJJbmdyZXNvIikpICsNCiAgeWxhYigiY2FtYmlvICUiKSArIHhsYWIoIkHDsW8iKSArDQogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJTZXJpZXMiKSkgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQpgYGANCg0KVW4gZGlhZ3JhbWEgZGUgZGlzcGVyc2nDs24gZW50cmUgYW1iYXMgc2VyaWVzLCBwYXJhIHZlciB1bmEgcG9zaWJsZSBjb3JyZWxhY2nDs24uDQoNCmBgYHtyfQ0KdXNfY2hhbmdlICU+JQ0KICBnZ3Bsb3QoYWVzKHg9SW5jb21lLCB5PUNvbnN1bXB0aW9uKSkgKw0KICAgIHlsYWIoIkNvbnN1bW8gKGNhbWJpbyAlIHRyaW1lc3RyYWwpIikgKw0KICAgIHhsYWIoIkluZ3Jlc28gKGNhbWJpbyAlIHRyaW1lc3RyYWwpIikgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFKQ0KYGBgDQoNCkxhIGzDrW5lYSBhenVsIGVuIGVsIGdyw6FmaWNvIHNpZ3VlIGxhIGVjdWFjacOzbiBxdWUgZGVzY3JpYmUgbGEgKipyZWdyZXNpw7NuIGxpbmVhbCoqIHF1ZSB0aWVuZSBwb3IgdmFyaWFibGUgZGVwZW5kaWVudGUgYWwgY29uc3VtbyB5IGNvbW8gaW5kZXBlbmRpZW50ZSBhbCBpbmdyZXNvLg0KDQojIyDCv1F1w6kgZXMgdW5hIHJlZ3Jlc2nDs24gbGluZWFsPw0KDQojIyMgDQoNCiFbXSguLi9pbWFnZXMvZWRfZWRkX2VkZGllLmdpZikNCg0KRWwgdMOpcm1pbm8gZGUgcmVncmVzacOzbiBmdWUgYWN1w7FhZG8gcG9yIHByaW1lcmEgdmV6IHBvciBGcmFuY2lzIEdhbHRvbiBlbiAxODg2LiDDiWwgZXN0YWJhIGVzdHVkaWFuZG8gbGEgcmVsYWNpw7NuIHF1ZSBleGlzdGUgZW50cmUgbGEgZXN0YXR1cmEgZGUgbG9zIGhpam9zIHkgbGEgZXN0YXR1cmEgZGUgbG9zIHBhZHJlcy4NCg0KTG8gcXVlIGVuY29udHLDsyBmdWUgbG8gc2lndWllbnRlLCBlbiByZXN1bWVuOg0KDQoqIExvcyBwYWRyZXMgbcOhcyBhbHRvcywgdGVuZMOtYW4gYSB0ZW5lciBoaWpvcyBtw6FzIGFsdG9zLCBtaWVudHJhcyBxdWUgbG9zIHBhZHJlcyBiYWpvcyB0ZW5kw61hbiBhIHRlbmVyIGhpam9zIGJham9zLg0KDQoqIEVuIHByb21lZGlvLCBsb3MgaGlqb3MgZGUgcGFkcmVzIGFsdG9zIG5vIGxvZ3JhbiBzZXIgbcOhcyBhbHRvcyBxdWUgZWxsb3MuIFNpbWlsYXJtZW50ZSwgbG9zIGhpam9zIGRlIHBhZHJlcyBiYWpvcywgZW4gcHJvbWVkaW8gc29uIG3DoXMgYWx0b3MgcXVlIHN1cyBwYXDDoXMuDQoNCiogQXPDrSwgR2FsdG9uIGRlY8OtYSBxdWUgaGFiw61hIHVuYSB0ZW5kZW5jaWEgYSAqKnJlZ3Jlc2FyKiogYSBsYSBlc3RhdHVyYSBwcm9tZWRpby4NCg0KDQoNCiFbXShpbWcvcmVncmVzc2lvbi5wbmcpDQoNCiFbKkRlIG5vIGN1bXBsaXJzZSBsYSByZWdyZXNpw7NuIGRlIEdhbHRvbiwgc2Vyw61hIGNvbcO6biB0ZW5lciBnZW50ZSBkZSBsYSBlc3RhdHVyYSBkZSB1biBIb2JiaXQsIHkgdGFtYmnDqW4gZGUgbGEgZXN0YXR1cmEgZGUgdW4gZ2lnYW50ZS4qXShpbWcvZmVsbG93c2hpcC5qcGcpDQoNCkVudG9uY2VzLCAqZWwgYW7DoWxpc2lzIGRlIHJlZ3Jlc2nDs24gZW4gdGllbXBvcyBtb2Rlcm5vcyB0cmF0YSBzb2JyZSBsYSByZWxhY2nDs24gZGUgbGEgZGVwZW5kZW5jaWEgZW50cmUgdW5hIHZhcmlhYmxlICR5JCwgcmVzcGVjdG8gZGUgdW5hIG8gbcOhcyB2YXJpYWJsZXMgZXjDs2dlbmFzIChyZWdyZXNvcmFzICR4JCkgcGFyYSBwcmVkZWNpciBlbCB2YWxvciBwcm9tZWRpbyBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZS4qDQoNCiMjIFJlZ3Jlc2nDs24geSBjYXVzYWxpZGFkDQoNCjxzdHlsZT4NCmRpdi5vcmNoaWQgeyBiYWNrZ3JvdW5kLWNvbG9yOiBvcmNoaWQ7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDsgY29sb3I6IGJsYWNrfQ0KPC9zdHlsZT4NCjxkaXYgY2xhc3MgPSAib3JjaGlkIj4NCg0KPuKAnFVuYSByZWxhY2nDs24gZXN0YWTDrXN0aWNhLCBwb3IgbcOhcyBmdWVydGUgeSBzdWdlcmVudGUgcXVlIHNlYSwgbnVuY2EgcG9kcsOhIGVzdGFibGVjZXIgdW5hIGNvbmV4acOzbiBjYXVzYWw6IG51ZXN0cmFzIGlkZWFzIGRlIGNhdXNhbGlkYWQgZGViZW4gcHJvdmVuaXIgZGUgZXN0YWTDrXN0aWNhcyBleHRlcm5hcyB5LCBlbiDDumx0aW1vIHTDqXJtaW5vLCBkZSB1bmEgdSBvdHJhIHRlb3LDrWHigJ0gKEtlbmRhbGwgJiBTdHVhcnQsIDE5NjEpDQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjtmb250LXdlaWdodDpib2xkIj4gDQpSZWdyZXNpw7NuICRcbmVxJCBDYXVzYWxpZGFkDQo8L3A+DQo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Zm9udC13ZWlnaHQ6Ym9sZCI+IA0KQ29ycmVsYWNpw7NuICRcbmVxJCBDYXVzYWxpZGFkDQo8L3A+DQoNCjwvZGl2Pg0KDQo8YnI+DQo8YnI+DQoNCiogVW5hIHJlbGFjacOzbiBlc3RhZMOtc3RpY2EgcG9yIHPDrSBtaXNtYSBubyBwdWVkZSBpbXBsaWNhciBjYXVzYWxpZGFkLg0KICAqIFNlIGRlYmUgYWN1ZGlyIGEgY29uc2lkZXJhY2lvbmVzIGEgcHJpb3JpIG8gdGXDs3JpY2FzLg0KDQo8YnI+DQoNCiogTGEgY2F1c2FsaWRhZCBwdWVkZSBkZXRlcm1pbmFyc2UgdGFtYmnDqW4gcG9yIHNlbnRpZG8gY29tw7puLg0KDQojIyDCv1F1w6kgc2lnbmlmaWNhIHF1ZSB1biBtb2RlbG8gc2VhIGxpbmVhbD8NCg0KU2UgcHVlZGUgaGFibGFyIGRlIGxpbmVhbGlkYWQgZW4gZG9zIHNlbnRpZG9zOg0KDQoxLiBMaW5lYWxpZGFkIGVuIGxhcyB2YXJpYWJsZXM7ICR4JC4NCiAgLSBMYSBlc3BlcmFuemEgY29uZGljaW9uYWwgZGUgJHkkIGVzIHVuYSBmdW5jacOzbiBsaW5lYWwgZGUgJHhfaSQuDQogIA0KMi4gTGluZWFsaWRhZCBlbiBsb3MgcGFyw6FtZXRyb3M7ICRcYmV0YSQuDQogIC0gTGEgZXNwZXJhbnphIGNvbmRpY2lvbmFsIGRlICR5JCBlcyBsaW5lYWwgZW4gbG9zIHBhcsOhbWV0cm9zICRcYmV0YV9pJC4NCg0KUGFyYSB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwsIHNvbG8gbm9zIGludGVyZXNhIHF1ZSBzZWEgKipsaW5lYWwgZW4gbG9zIHBhcsOhbWV0cm9zKiouDQoNCiFbKlRvZGFzIGVzdGFzIGZ1bmNpb25lcyBzb24gKipsaW5lYWxlcyBlbiBsb3MgcGFyw6FtZXRyb3MqKiB5IHB1ZWRlbiBzZXIgZXN0aW1hZGFzIG1lZGlhbnRlIHVuIG1vZGVsbyBkZSByZWdyZXNpw7NuIGxpbmVhbCpdKGltZy9saW5lYXIucG5nKQ0KDQpBc8OtLCB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgcHVlZGUgZ2VuZXJhciB1bmEgcmVjdGEsIG8gdW5hIHZhcmllZGFkIGRlIGN1cnZhcywgZGVwZW5kaWVuZG8gbGEgKipmb3JtYSBmdW5jaW9uYWwqKiBxdWUgc2UgZWxpamEuDQoNCiMjIE3DrW5pbW9zIGN1YWRyYWRvcyBvcmRpbmFyaW9zIChNQ08tT0xTKQ0KDQoNCg0KJCQNCkhfMDogXGJldGFfMCA9IDAgXFwNCkhfMTogXGJldGFfMCBcbmVxIDANCiQkDQokJA0KSF8wOiBcYmV0YV8xID0gMCBcXA0KSF8xOiBcYmV0YV8xIFxuZXEgMA0KJCQNCg0KDQokJA0KXGhhdHt5fV90ID0gXGhhdHtcYmV0YX1fMCArIFxoYXR7XGJldGF9XzF4X3QgKyBcaGF0e1x2YXJlcHNpbG9ufV90IFxcDQpcaGF0e3l9X3QgPSAwLjU0NDU0ICsgMC4yNzE4MyB4X3QgKyBcaGF0e1x2YXJlcHNpbG9ufV90DQokJA0KDQokJA0KeV97Y29uc3Vtb30gPSBcYmV0YV8wICsgXGJldGFfMSB4X3tpbmNvbWV9ICsgXGJldGFfMiB4X3twcm9kdWN0aW9ufSArIFxiZXRhXzMgeF97c2F2aW5nc30gKyBcYmV0YV80IHhfe3VuZW1wbG95bWVudH0NCiQkDQoNCg0KIyBFamVtcGxvcw0KDQojIyBVUyAlIGNoYW5nZQ0KDQpgYGB7cn0NCnVzX2NoYW5nZQ0KYGBgDQoNCmBgYHtyfQ0KdXNfY2hhbmdlICU+JSANCiAgYXNfdGliYmxlKCkgJT4lIA0KICBzZWxlY3QoLVF1YXJ0ZXIpICU+JSANCiAgR0dhbGx5OjpnZ3BhaXJzKCkNCmBgYA0KDQoqKlVuYSBjb3JyZWxhY2nDs24sIHBvciBtw6FzIGZ1ZXJ0ZSBxdWUgc2VhIGVudHJlIGRvcyB2YXJpYWJsZXMsIG5vIHB1ZWRlIGltcGxpY2FyIHBvciBzw60gbWlzbWEgY2F1c2FsaWRhZC4qKg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gLVF1YXJ0ZXIpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gUXVhcnRlciwgeSA9IHZhbHVlLCBjb2xvciA9IG5hbWUpKSArDQogIGdlb21fbGluZSgpICsNCiAgZmFjZXRfd3JhcCh+IG5hbWUsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpHcmFmaWNhbmRvIG51ZXN0cmEgdmFyaWFibGUgZGUgcHJvbsOzc3RpY28gKENvbnN1bW8pIHZzLiBjYWRhIHVuYSBkZSBsYXMgdmFyaWFibGVzIHByZWRpY3RvcmFzOg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gLWMoUXVhcnRlciwgQ29uc3VtcHRpb24pKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIsIHkgPSB2YWx1ZSwgY29sb3IgPSBuYW1lKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fbGluZShhZXMoeSA9IENvbnN1bXB0aW9uKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGZhY2V0X3dyYXAofiBuYW1lLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KDQojIyMgUmVncmVzacOzbiBsaW5lYWwgc2ltcGxlDQoNClJlYWxpemFtb3MgdW4gcHJpbWVyIG1vZGVsbywgZG9uZGUgdXRpbGl6YXJlbW9zIGRlIHZhcmlhYmxlIHByZWRpY3RvcmEgYWwgaW5ncmVzbyBkaXNwb25pYmxlLCBwYXJhIHByb25vc3RpY2FyIGVsIGNvbnN1bW8uDQoNCiQkDQp5X3t0LENvbnN1bW99ID0gXGJldGFfMCArIFxiZXRhX3sxfXhfe3QsSW5ncmVzb30gKyBcdmFyZXBzaWxvbl90DQokJA0KDQpgYGB7cn0NCmZpdDEgPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwocmVnX2xpbl9zaW1wbGUgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lKQ0KICAgICAgICApDQpmaXQxICU+JSAgcmVwb3J0KCkNCmBgYA0KDQpQcnVlYmEgZGUgc2lnbmlmaWNhbmNpYSBpbmRpdmlkdWFsIHBhcmEgY2FkYSB1bm8gZGUgbG9zIHBhcsOhbWV0cm9zICgkXGJldGFfaSQpDQoNCiQkIA0KSF8wOiBcYmV0YV9pID0gMA0KJCQNCg0KUHJ1ZWJhIGRlIHNpZ25pZmljYW5jaWEgY29uanVudGEgKHBhcmEgcmV2aXNhciBzaSBudWVzdHJvIG1vZGVsbyBzaXJ2ZSBvIG5vKQ0KDQokJA0KSF8wOiBcYmV0YV8xID0gXGJldGFfMiA9IFxiZXRhXzMgPSBcbGRvdHMgPSAwDQokJA0KDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQxKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IENvbnN1bXB0aW9uLCBjb2xvciA9ICJEYXRvcyIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG9yID0gIkZpdHRlZCIpKSsNCiAgeGxhYigiQcOxbyIpICsgeWxhYihOVUxMKSArDQogIGdndGl0bGUoIkNhbWJpb3MgcG9yY2VudHVhbGVzIGVuIGVsIGdhc3RvIGRlIENvbnN1bW8gZW4gRUVVVSIpICsNCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpDQpgYGANCg0KRWwgbW9kZWxvIG5vIHBhcmVjZSBjYXB0dXJhciBhZGVjdWFkYW1lbnRlIGxhIHZhcmlhY2nDs24gZGUgbG9zIGRhdG9zIHJlYWxlcy4NCg0KYGBge3J9DQphdWdtZW50KGZpdDEpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gQ29uc3VtcHRpb24sIHkgPSAuZml0dGVkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB5bGFiKCJGaXR0ZWQgKHZhbG9yZXMgYWp1c3RhZG9zKSIpICsNCiAgeGxhYigiRGF0b3MgKHJlYWxlcyBoaXN0w7NyaWNvcykiKSArDQogIGdndGl0bGUoIkNhbWJpb3MgcG9yY2VudHVhbGVzIGVuIGVsIGdhc3RvIGRlIENvbnN1bW8gZW4gRUVVVSIpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKQ0KYGBgDQoNCmBgYHtyfQ0KZml0MSAlPiUgDQogIGdnX3RzcmVzaWR1YWxzKCkNCmBgYA0KDQpgYGB7cn0NCmF1Z21lbnQoZml0MSkgJT4lIA0KICBmZWF0dXJlcygucmVzaWQsIGxqdW5nX2JveCwgbGFnPSAxMCwgZG9mID0gMikNCmBgYA0KRXMgZXZpZGVudGUgcXVlIGVzdGUgbW9kZWxvIHNlIHB1ZWRlIG1lam9yYXIuIFByb2JlbW9zIGluY2x1eWVuZG8gbGFzIG90cmFzIHByZWRpY3RvcmFzLg0KDQojIyBSZWdyZXNpw7NuIGxpbmVhbCBtw7psdGlwbGUNCg0KUG9kcsOtYW1vcyBwcm9wb25lciBhaG9yYSB1biBtb2RlbG8gZW4gZG9uZGUgZWwgY29uc3VtbyBzZWEgdW5hIGZ1bmNpw7NuIGRlbCBpbmdyZXNvLCBsYSBwcm9kdWNjacOzbiwgbG9zIGFob3Jyb3MgeSBlbCBkZXNlbXBsZW86DQoNCiQkDQp5X3t0LENvbnN1bW99ID0gXGJldGFfMCArIFxiZXRhX3sxfXhfe3QsSW5ncmVzb30gKyBcYmV0YV8yeF97dCxQcm9kdWNjacOzbn0gKyBcYmV0YV8zeF97dCxBaG9ycm9zfSArIFxiZXRhXzR4X3t0LERlc2VtcGxlb30gKyBcdmFyZXBzaWxvbl90DQokJA0KDQpgYGB7cn0NCmZpdDIgPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwoDQogICAgcmVnX2xpbl9tdWx0aXBsZSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyArIFVuZW1wbG95bWVudCkNCiAgKQ0KcmVwb3J0KGZpdDIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IENvbnN1bXB0aW9uLCBjb2xvciA9ICJEYXRvcyIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG9yID0gIkZpdHRlZCIpKSsNCiAgeGxhYigiQcOxbyIpICsgeWxhYihOVUxMKSArDQogIGdndGl0bGUoIkNhbWJpb3MgcG9yY2VudHVhbGVzIGVuIGVsIGdhc3RvIGRlIENvbnN1bW8gZW4gRUVVVSIpICsNCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpDQpgYGANCg0KRXN0ZSBtb2RlbG8gcGFyZWNlIGNhcHR1cmFyIG3DoXMgdmFyaWFjacOzbiBkZSBsb3MgZGF0b3MgaGlzdMOzcmljb3MuDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IENvbnN1bXB0aW9uLCB5ID0gLmZpdHRlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeWxhYigiRml0dGVkICh2YWxvcmVzIGFqdXN0YWRvcykiKSArDQogIHhsYWIoIkRhdG9zIChyZWFsZXMgaGlzdMOzcmljb3MpIikgKw0KICBnZ3RpdGxlKCJDYW1iaW9zIHBvcmNlbnR1YWxlcyBlbiBlbCBnYXN0byBkZSBDb25zdW1vIGVuIEVFVVUiKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSkNCmBgYA0KDQpgYGB7cn0NCmZpdDIgJT4lIA0KICBnZ190c3Jlc2lkdWFscygpDQpgYGANCg0KYGBge3J9DQphdWdtZW50KGZpdDIpICU+JSANCiAgZmVhdHVyZXMoLnJlc2lkLCBsanVuZ19ib3gsIGxhZz0gMTAsIGRvZiA9IDIpDQpgYGANCg0KDQpgYGB7cn0NCmRmIDwtIGxlZnRfam9pbih1c19jaGFuZ2UsIHJlc2lkdWFscyhmaXQyKSwgYnkgPSAiUXVhcnRlciIpDQpkZiAlPiUgDQogIHNlbGVjdCgtYyhDb25zdW1wdGlvbiwgLm1vZGVsKSkgJT4lIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGMoSW5jb21lOlVuZW1wbG95bWVudCkpICU+JSANCiAgZ2dwbG90KGFlcyggeCA9IHZhbHVlLCB5ID0gLnJlc2lkLCBjb2xvciA9IG5hbWUpKSArIA0KICBnZW9tX3BvaW50KCkgKyB5bGFiKCJSZXNpZHVhbGVzIikgKyB4bGFiKCJQcmVkaWN0b3JhcyIpICsNCiAgZmFjZXRfd3JhcCh+IG5hbWUsIHNjYWxlcyA9ICJmcmVlX3giKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpgYGB7cn0NCmF1Z21lbnQoZml0MikgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSAuZml0dGVkLCB5ID0gLnJlc2lkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHggPSAiQWp1c3RhZG9zIiwgeSA9ICJSZXNpZHVhbGVzIikNCmBgYA0KDQpgYGB7cn0NCmdsYW5jZShmaXQyKSAlPiUgDQogIHNlbGVjdChhZGpfcl9zcXVhcmVkLCBBSUMsIEFJQ2MsIEJJQykNCmBgYA0KDQoNCg0KIyMgU2VsZWNjacOzbiBkZSBwcmVkaWN0b3JhcyB7LnRhYnNldH0NCg0KIyMjIDEuIEVzY29nZXIgc3ViY29uanVudG9zIGRlIHByZWRpY3RvcmFzIHkgcHJvYmFybG8uDQoNCmBgYHtyfQ0KZml0MyA8LSB1c19jaGFuZ2UgJT4lIA0KICBtb2RlbChyMSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUpLA0KICAgICAgICByMiA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uKSwNCiAgICAgICAgcjMgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgUHJvZHVjdGlvbiArIFNhdmluZ3MgKyBVbmVtcGxveW1lbnQpLA0KICAgICAgICByNCA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyksDQogICAgICAgIHI1ID0gVFNMTShDb25zdW1wdGlvbiB+IEluY29tZSArIFNhdmluZ3MgKyBVbmVtcGxveW1lbnQpLA0KICAgICAgICByNiA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgcjcgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgU2F2aW5ncykNCiAgICAgICAgKQ0KZml0MyAlPiUgDQogIGdsYW5jZSgpICU+JSANCiAgc2VsZWN0KC5tb2RlbCwgYWRqX3Jfc3F1YXJlZCwgQUlDLCBBSUNjLCBCSUMpDQpgYGANCg0KYGBge3J9DQpmaXQzICU+JSANCiAgc2VsZWN0KHIzKSAlPiUgDQogIHJlcG9ydCgpDQpgYGANCg0KIyMjIDIuIEJhY2t3YXJkcyBzdGVwd2lzZSByZWdyZXNzaW9uOg0KICAtIEVtcGV6YW1vcyBjb24gdW4gbW9kZWxvIHF1ZSBjb250ZW5nYSB0b2RhcyBsYXMgcHJlZGljdG9yYXMuDQogIC0gUXVpdGFtb3MgdW5hIGEgbGEgdmV6Lg0KICAtIE1hbnRlbmVtb3MgZWwgbW9kZWxvIHNpIG1lam9yYSBsYSBtZWRpZGEgZGUgZGVzZW1wZcOxbyBwcmVkaWN0aXZvICgkXGJhcntSfV4yJCwgQUlDYywuLi4pLg0KICAtIFNlZ3VpcmxvIGhhY2llbmRvIGhhc3RhIG5vIGVuY29udHJhciBtZWpvcmFzIGFkaWNpb25hbGVzLg0KDQpTZWxlY2Npw7NuIGRlbCBtb2RlbG8gYSB0cmF2w6lzIGRlICpiYWNrd2FyZHMgc3RlcHdpc2UgcmVncmVzc2lvbiogY29uIGJhc2UgZW4gZWwgJEFJQ2MkIHN1Z2llcmUgY29uc2VydmFyIGVsIG1vZGVsbyBxdWUgaW5jbHV5ZSAqKnRvZGFzIGxhcyBwcmVkaWN0b3JhcyoqLg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIG1vZGVsKGkgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgUHJvZHVjdGlvbiArIFNhdmluZ3MgKyBVbmVtcGxveW1lbnQpLA0KICAgICAgICBpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyksDQogICAgICAgIGlpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgaXYgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgU2F2aW5ncyArIFVuZW1wbG95bWVudCksDQogICAgICAgIHYgPSBUU0xNKENvbnN1bXB0aW9uIH4gUHJvZHVjdGlvbiArIFNhdmluZ3MgKyBVbmVtcGxveW1lbnQpDQogICAgICAgICkgJT4lIA0KICBnbGFuY2UoKSAlPiUgDQogIHNlbGVjdCgubW9kZWwsIGFkal9yX3NxdWFyZWQsIEFJQywgQUlDYywgQklDKQ0KYGBgDQoNCkNvbiBiYXNlIGVuICRCSUMkIHBhcmVjZSBxdWUgZWwgbWVqb3IgbW9kZWxvIGVzIGVsIHF1ZSBpbmNsdXllIHRvZGFzIGxhcyBwcmVkaWN0b3JhcyAqKm1lbm9zIGVsIGRlc2VtcGxlbyoqLg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIG1vZGVsKGkgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgUHJvZHVjdGlvbiArIFNhdmluZ3MgKyBVbmVtcGxveW1lbnQpLA0KICAgICAgICBpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyksDQogICAgICAgIGlpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uKSwNCiAgICAgICAgaXYgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgU2F2aW5ncyksDQogICAgICAgIHYgPSBUU0xNKENvbnN1bXB0aW9uIH4gUHJvZHVjdGlvbiArIFNhdmluZ3MpLA0KICAgICAgICB2aSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgdmlpID0gVFNMTShDb25zdW1wdGlvbiB+IFByb2R1Y3Rpb24gKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgdmlpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgVW5lbXBsb3ltZW50KQ0KICAgICAgICApICU+JSANCiAgZ2xhbmNlKCkgJT4lIA0KICBzZWxlY3QoLm1vZGVsLCBhZGpfcl9zcXVhcmVkLCBBSUMsIEFJQ2MsIEJJQykNCmBgYA0KDQoNCiMjIyAzLiBGb3J3YXJkcyBzdGVwd2lzZSByZWdyZXNzaW9uOg0KICAtIENvbWVuemFyIGNvbiB1biBtb2RlbG8gcXVlIHNvbG8gaW5jbHV5YSBhbCBpbnRlcmNlcHRvLg0KICAtIFNlIHZhbiBhZ3JlZ2FuZG8gbGFzIHByZWRpY3RvcmFzIHVuYSBhIGxhIHZlei4NCiAgLSBMYSBwcmVkaWN0b3JhIHF1ZSBtZWpvcmUgbcOhcyBhbCBtb2RlbG8gc2UgbWFudGllbmUuDQogIC0gU2UgaXRlcmEgaGFzdGEgbm8gdGVuZXIgbWVqb3LDrWEgYWRpY2lvbmFsLg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIG1vZGVsKGkgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lKSwNCiAgICAgICAgaWkgPSBUU0xNKENvbnN1bXB0aW9uIH4gUHJvZHVjdGlvbiksDQogICAgICAgIGlpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBTYXZpbmdzKSwNCiAgICAgICAgaXYgPSBUU0xNKENvbnN1bXB0aW9uIH4gVW5lbXBsb3ltZW50KQ0KICAgICAgICApICU+JSANCiAgZ2xhbmNlKCkgJT4lIA0KICBzZWxlY3QoLm1vZGVsLCBhZGpfcl9zcXVhcmVkLCBBSUMsIEFJQ2MsIEJJQykNCmBgYA0KRWwgbWVqb3IgbW9kZWxvIGhhc3RhIGFob3JhIGVzIGVsIHF1ZSB0b21hIGRlIHByZWRpY3RvcmEgYSBsYSBwcm9kdWNjacOzbi4NCg0KYGBge3J9DQp1c19jaGFuZ2UgJT4lIA0KICBtb2RlbCgNCiAgICBvICAgPSBUU0xNKENvbnN1bXB0aW9uIH4gUHJvZHVjdGlvbiksDQogICAgaSAgID0gVFNMTShDb25zdW1wdGlvbiB+IFByb2R1Y3Rpb24gKyBJbmNvbWUpLA0KICAgIGlpICA9IFRTTE0oQ29uc3VtcHRpb24gfiBQcm9kdWN0aW9uICsgU2F2aW5ncyksDQogICAgaWlpID0gVFNMTShDb25zdW1wdGlvbiB+IFByb2R1Y3Rpb24gKyBVbmVtcGxveW1lbnQpDQogICkgJT4lIA0KICBnbGFuY2UoKSAlPiUgDQogIHNlbGVjdCgubW9kZWwsIGFkal9yX3NxdWFyZWQsIEFJQywgQUlDYywgQklDKQ0KYGBgDQpFbCBtZWpvciBoYXN0YSBhaG9yYSBlcyBlbCBtb2RlbG8gY29uIGxhIHByb2R1Y2Npw7NuIHkgZWwgaW5ncmVzby4NCg0KYGBge3J9DQp1c19jaGFuZ2UgJT4lIA0KICBtb2RlbCgNCiAgICBpICAgPSBUU0xNKENvbnN1bXB0aW9uIH4gUHJvZHVjdGlvbiArIEluY29tZSksDQogICAgaWkgID0gVFNMTShDb25zdW1wdGlvbiB+IFByb2R1Y3Rpb24gKyBJbmNvbWUgKyBTYXZpbmdzKSwNCiAgICBpaWkgPSBUU0xNKENvbnN1bXB0aW9uIH4gUHJvZHVjdGlvbiArIEluY29tZSArIFVuZW1wbG95bWVudCkNCiAgKSAlPiUgDQogIGdsYW5jZSgpICU+JSANCiAgc2VsZWN0KC5tb2RlbCwgYWRqX3Jfc3F1YXJlZCwgQUlDLCBBSUNjLCBCSUMpDQpgYGANCkVsIG1vZGVsbyBtZWpvcmEgYWdyZWdhbmRvIGxvcyBhaG9ycm9zIHRhbWJpw6luLg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIG1vZGVsKA0KICAgIGkgICA9IFRTTE0oQ29uc3VtcHRpb24gfiBQcm9kdWN0aW9uICsgSW5jb21lICsgU2F2aW5ncyksDQogICAgaWkgID0gVFNMTShDb25zdW1wdGlvbiB+IFByb2R1Y3Rpb24gKyBJbmNvbWUgKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KQ0KICApICU+JSANCiAgZ2xhbmNlKCkgJT4lIA0KICBzZWxlY3QoLm1vZGVsLCBhZGpfcl9zcXVhcmVkLCBBSUMsIEFJQ2MsIEJJQykNCmBgYA0KDQpVdGlsaXphbmRvIGxhIGBhZGpfcl9zcXVhcmVkYCBvIGVsIGBBSUNjYCwgY29uY2x1aW1vcyBsbyBtaXNtbyBxdWUgYW50ZXM6IGVsIG1vZGVsbyBxdWUgaW5jbHV5ZSBhIHRvZGFzIGxhcyB2YXJpYWJsZXMgZXMgZWwgbWVqb3IuDQojIyMgey19DQoNCiMjIFByb27Ds3N0aWNvDQoNCiMjIyBQcm9uw7NzdGljb3MgKmV4LWFudGUqDQoNCkVuIGVzdG9zIHByb27Ds3N0aWNvcyBzb2xvIHNlIHV0aWxpemEgaW5mb3JtYWNpw7NuIGRpc3BvbmlibGUgaGFzdGEgZWwgw7psdGltbyBkYXRvIGRlbCBoaXN0w7NyaWNvLiBBIGVzdG9zIHByb27Ds3N0aWNvcyBzZSBsZXMgY29uc2lkZXJhIGNvbW8gKipwcm9uw7NzdGljb3MgcmVhbGVzKiouIEFxdcOtIGxhcyBwcmVkaWN0b3JhcyBzZSBkZWJlbiBwcm9ub3N0aWNhciBhbnRlcyBkZSBwb2RlciBwcm9kdWNpciBlbCBwcm9uw7NzdGljbyBkZSBsYSB2YXJpYWJsZSBkZSBpbnRlcsOpcy4NCg0KIyMjIFByb27Ds3N0aWNvcyAqZXgtcG9zdCoNCg0KQ29uIGVzdG9zIHByb27Ds3N0aWNvcyBzZSB1dGlsaXphIGluZm9ybWFjacOzbiByZWFsIGRpc3BvbmlibGUgZGUgbGFzIHByZWRpY3RvcmFzLiBFc3RvcyBwcm9uw7NzdGljb3MgeWEgbm8gc29uICoqcmVhbGVzKiogKGVuIGVsIHNlbnRpZG8gZXN0cmljdG8pLiBMYSB2YXJpYWJsZSBhIHByb25vc3RpY2FyICgkeSQpIHNpZ3VlIHNpZW5kbyBkZXNjb25vY2lkYS4NCg0KIyMjIFByb27Ds3N0aWNvcyBiYXNhZG9zIGVuIGVzY2VuYXJpb3MNCg0KYGBge3IgcGFnZWQucHJpbnQ9VFJVRX0NCmZpdF9lc2NlbmFyaW9zIDwtIHVzX2NoYW5nZSAlPiUgDQogIG1vZGVsKGxpbmVhbCA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KSkNCg0KIyBOZWNlc2l0YW1vcyBhZ3JlZ2FyIG51ZXZvcyBkYXRvcyBkZSBsYXMgcHJlZGljdG9yYXMNCmVzY2VuYXJpb3MgPC0gc2NlbmFyaW9zKA0KICBvcHRpbWlzdGEgPSBuZXdfZGF0YSh1c19jaGFuZ2UsIDQpICU+JSANCiAgICBtdXRhdGUoSW5jb21lID0gYygwLDAuMiwwLDEuMiksIFNhdmluZ3MgPSBjKDAsLTAuMSwwLjUsLTEpLCBVbmVtcGxveW1lbnQgPSAtMC4xKSwNCiAgcGVzaW1pc3RhID0gbmV3X2RhdGEodXNfY2hhbmdlLCA0KSAlPiUgDQogICAgbXV0YXRlKEluY29tZSA9IC0xLCBTYXZpbmdzID0gLTAuNSwgVW5lbXBsb3ltZW50ID0gMC4xKSwNCiAgbmFtZXNfdG8gPSAiRXNjZW5hcmlvIg0KKQ0KDQpmY19lc2NlbmFyaW9zIDwtIGZpdF9lc2NlbmFyaW9zICU+JSANCiAgZm9yZWNhc3QobmV3X2RhdGEgPSBlc2NlbmFyaW9zKQ0KDQp1c19jaGFuZ2UgJT4lIA0KICBhdXRvcGxvdChDb25zdW1wdGlvbikgKw0KICBhdXRvbGF5ZXIoZmNfZXNjZW5hcmlvcykNCmBgYA0KDQpWYW1vcyBhIGdlbmVyYXIgdW4gcHJvbsOzc3RpY28gKmV4LWFudGUqIGRlbCBjb25zdW1vLiBQYXJhIGVzdG8sIG5lY2VzaXRhbW9zIHByaW1lcm8gcHJvZHVjaXIgcHJvbsOzc3RpY29zIGRlIGxhcyBwcmVkaWN0b3JhczoNCg0KYGBge3J9DQojIG1vZF9wcmVkaWN0b3JhcyA8LSBmdW5jdGlvbihwcmVkaWN0b3JhLCBob3Jpem9udGUgPSA0KSB7DQojICAgdXNfY2hhbmdlICU+JSANCiMgICAgIG1vZGVsKHByZWRpY3RvcmEgPSBBUklNQShhcy5mb3JtdWxhKHByZWRpY3RvcmEpKSAlPiUgDQojICAgICBmb3JlY2FzdChoID0gaG9yaXpvbnRlKQ0KIyB9DQojIA0KIyBtb2RfcHJlZGljdG9yYXMocHJlZGljdG9yYSA9IEluY29tZSkNCg0KaW5ncmVzbyA8LSAgdXNfY2hhbmdlICU+JSANCiAgbW9kZWwoRVRTID0gRVRTKEluY29tZSksDQogICAgICAgIEFSSU1BID0gQVJJTUEoSW5jb21lKQ0KICAgICAgICApICU+JSANCiAgZm9yZWNhc3QoaCA9IDQpIA0KDQppbmdyZXNvICU+JSANCiAgYXV0b3Bsb3QodXNfY2hhbmdlLCBsZXZlbCA9IE5VTEwpDQoNCnByb2R1Y2Npb24gPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwoDQogICAgRVRTID0gRVRTKFByb2R1Y3Rpb24pLA0KICAgIEFSSU1BID0gQVJJTUEoUHJvZHVjdGlvbikNCiAgKSAlPiUgDQogIGZvcmVjYXN0KGggPSA0KQ0KcHJvZHVjY2lvbiAlPiUgDQogIGF1dG9wbG90KHVzX2NoYW5nZSwgbGV2ZWwgPSBOVUxMKQ0KDQphaG9ycm8gPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwoDQogICAgRVRTID0gRVRTKFNhdmluZ3MpLA0KICAgIEFSSU1BID0gQVJJTUEoU2F2aW5ncykNCiAgKSAlPiUgDQogIGZvcmVjYXN0KGggPSA0KQ0KYWhvcnJvICU+JSANCiAgYXV0b3Bsb3QodXNfY2hhbmdlLCBsZXZlbCA9IE5VTEwpDQoNCmRlc2VtcGxlbyA8LSB1c19jaGFuZ2UgJT4lIA0KICBtb2RlbCgNCiAgICBFVFMgPSBFVFMoVW5lbXBsb3ltZW50KSwNCiAgICBBUklNQSA9IEFSSU1BKFVuZW1wbG95bWVudCkNCiAgKSAlPiUgDQogIGZvcmVjYXN0KGggPSA0KQ0KZGVzZW1wbGVvICU+JSANCiAgYXV0b3Bsb3QodXNfY2hhbmdlLCBsZXZlbCA9IE5VTEwpDQpgYGANCg0KVGVuaWVuZG8geWEgbG9zIHByb27Ds3N0aWNvcyBkZSBjYWRhIHByZWRpY3RvcmEsIHBvZGVtb3MgcHJvY2VkZXIgYSBnZW5lcmFyIGVsIHByb27Ds3N0aWNvIGRlbCBjb25zdW1vLg0KDQpgYGB7cn0NCmZpdCA8LSB1c19jaGFuZ2UgJT4lIA0KICBtb2RlbCgNCiAgICBgUmVncmVzacOzbiBsaW5lYWwgbcO6bHRpcGxlYCA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyArIFVuZW1wbG95bWVudCkNCiAgKQ0KDQpkYXRvc19mdXR1cm9zIDwtIG5ld19kYXRhKHVzX2NoYW5nZSw0KSAlPiUgDQogIG11dGF0ZShJbmNvbWUgPSBpbmdyZXNvICU+JSBmaWx0ZXIoLm1vZGVsID09ICJBUklNQSIpICU+JSBwdWxsKC5tZWFuKSwgDQogICAgICAgICBTYXZpbmdzID0gYWhvcnJvICU+JSBmaWx0ZXIoLm1vZGVsID09ICJBUklNQSIpICU+JSBwdWxsKC5tZWFuKSwgDQogICAgICAgICBVbmVtcGxveW1lbnQgPSBkZXNlbXBsZW8gJT4lIGZpbHRlcigubW9kZWwgPT0gIkFSSU1BIikgJT4lIHB1bGwoLm1lYW4pLA0KICAgICAgICAgUHJvZHVjdGlvbiA9IHByb2R1Y2Npb24gJT4lIGZpbHRlcigubW9kZWwgPT0gIkFSSU1BIikgJT4lIHB1bGwoLm1lYW4pKQ0KDQpkYXRvc19mdXR1cm9zDQoNCmZjIDwtIGZvcmVjYXN0KGZpdCwgZGF0b3NfZnV0dXJvcykNCg0KZmMgJT4lIA0KICBhdXRvcGxvdCh1c19jaGFuZ2UpDQpmYyAlPiUgDQogIGF1dG9wbG90KHVzX2NoYW5nZSAlPiUgZmlsdGVyX2luZGV4KCIyMDE2IFExIiB+IC4pKQ0KYGBgDQoNCg0KDQojIyBJbmNsdXNpw7NuIGRlIHByZWRpY3RvcmFzIMO6dGlsZXM6IFByb2R1Y2Npw7NuIGRlIGNlcnZlemENCg0KYGBge3J9DQpyZWNlbnRfcHJvZHVjdGlvbiA8LSBhdXNfcHJvZHVjdGlvbiAlPiUgDQogIGZpbHRlcih5ZWFyKFF1YXJ0ZXIpID49IDE5OTIpDQoNCnJlY2VudF9wcm9kdWN0aW9uDQoNCnJlY2VudF9wcm9kdWN0aW9uICU+JSANCiAgYXV0b3Bsb3QoQmVlcikgKw0KICBsYWJzKHggPSAiQcOxbyIsIHkgPSAiTWVnYWxpdHJvcyIsIA0KICAgICAgIHRpdGxlID0gIlByb2R1Y2Npw7NuIGRlIGNlcnZlemEgdHJpbWVzdHJhbCBlbiBBdXN0cmFsaWEiKQ0KYGBgDQoNCkV4aXN0ZW4gdmFyaWFzIHByZWRpY3RvcmFzIHF1ZSBwdWVkZW4gc2VyIMO6dGlsZXMgZW4gZWwgYW7DoWxpc2lzIGRlIHJlZ3Jlc2nDs24uDQoNCjEuIFByZWRpY3RvcmEgZGUgdGVuZGVuY2lhIGB0cmVuZCgpYA0KDQokJA0KeV90ID0gXGJldGFfMCArIFxiZXRhXzF0ICsgXHZhcmVwc2lsb25fdA0KJCQNCg0KMi4gVmFyaWFibGVzIGR1bW15IGVzdGFjaW9uYWxlcw0KDQpMYXMgdmFyaWFibGVzIGR1bW15ICh0YW1iacOpbiBsbGFtYWRhcyBkaWNvdMOzbWljYXMsIGJpbmFyaWFzLCAuLi4pIHRvbWFuIHNvbG8gZG9zIHZhbG9yZXM6ICoqMSoqIG8gKiowKiogKHZlcmRhZGVyby9mYWxzbykuDQoNClBhcmEgYWdyZWdhciB2YXJpYWJsZXMgZHVtbXkgZXN0YWNpb25hbGVzLCBiYXN0YSBjb24gZXNjcmliaXIgYHNlYXNvbigpYC4NCg0KYGBge3J9DQpyZWNlbnRfcHJvZHVjdGlvbiAlPiUgDQogIHNlbGVjdChRdWFydGVyLEJlZXIpICU+JSANCiAgbXV0YXRlKHRlbmRlbmNpYSA9IHNlcV9hbG9uZyhyZWNlbnRfcHJvZHVjdGlvbiRRdWFydGVyKSwNCiAgICAgICAgIHEyID0gaWZfZWxzZShxdWFydGVyKFF1YXJ0ZXIpPT0yLDEsMCksDQogICAgICAgICBxMyA9IGlmX2Vsc2UocXVhcnRlcihRdWFydGVyKT09MywxLDApLA0KICAgICAgICAgcTQgPSBpZl9lbHNlKHF1YXJ0ZXIoUXVhcnRlcik9PTQsMSwwKQ0KICAgICAgICAgKSAlPiUgDQogIG1vZGVsKFRTTE0oQmVlciB+IHRlbmRlbmNpYSArIHEyICsgcTMgKyBxNCkpICU+JSANCiAgcmVwb3J0KCkNCmBgYA0KDQoNCmBgYHtyfQ0KZml0X2JlZXIgPC0gcmVjZW50X3Byb2R1Y3Rpb24gJT4lIA0KICBtb2RlbChUU0xNKEJlZXIgfiB0cmVuZCgpICsgc2Vhc29uKCkpKQ0KDQpyZXBvcnQoZml0X2JlZXIpDQpgYGANCg0KYGBge3J9DQpwIDwtIGF1Z21lbnQoZml0X2JlZXIpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gUXVhcnRlcikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gQmVlciwgY29sb3IgPSAiRGF0b3MiKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSAuZml0dGVkLCBjb2xvciA9ICJGaXR0ZWQiKSkgKw0KICBsYWJzKHggPSAiQcOxbyIsIHkgPSAiTWVnYWxpdHJvcyIpDQoNCmdncGxvdGx5KHApDQpgYGANCg0KQWwgdmVyIGxhIGdyw6FmaWNhLCB2ZW1vcyBxdWUgZXhpc3RlIHVuIHRyaW1lc3RyZSBtdXkgcG9yIGRlYmFqbyBkZWwgcmVzdG8uIFBvZGVtb3MgYWdyZWdhciB1bmEgKip2YXJpYWJsZSBkZSBpbnRlcnZlbmNpw7NuKiogcGFyYSBlc2UgcGVyaW9kby4NCg0KIyMjIFNwaWtlIHZhcmlhYmxlcw0KDQpDYXB0dXJhbiBlbCBlZmVjdG8gZGUgdW4gc29sbyBwZXJpb2RvLg0KDQpgYGB7cn0NCmNlcnZlemEgPC0gcmVjZW50X3Byb2R1Y3Rpb24gJT4lIA0KICBzZWxlY3QoUXVhcnRlciwgQmVlcikgJT4lIA0KICBtdXRhdGUoDQogICAgcTJfOTQgPSBpZl9lbHNlKFF1YXJ0ZXIgPT0geWVhcnF1YXJ0ZXIoIjE5OTQgUTIiKSwxLDApLA0KICAgIHE0XzA0ID0gaWZfZWxzZShRdWFydGVyID09IHllYXJxdWFydGVyKCIyMDA0IFE0IiksMSwwKQ0KICApDQpjZXJ2ZXphDQpgYGANCg0KQWp1c3RhbW9zIHVuIG1vZGVsbywgY29ycmlnaWVuZG8gcG9yIGVzZSBwZXJpb2RvIG91dGxpZXIgKDE5OTQgUTIpLg0KDQpgYGB7cn0NCmZpdF9iZWVyIDwtIGNlcnZlemEgJT4lIA0KICBtb2RlbChUU0xNKEJlZXIgfiB0cmVuZCgpICsgc2Vhc29uKCkgKyBxMl85NCArIHE0XzA0KSkNCg0KcmVwb3J0KGZpdF9iZWVyKQ0KYGBgDQoNCmBgYHtyfQ0KcCA8LSBhdWdtZW50KGZpdF9iZWVyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEJlZXIsIGNvbG9yID0gIkRhdG9zIikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3IgPSAiRml0dGVkIikpICsNCiAgbGFicyh4ID0gIkHDsW8iLCB5ID0gIk1lZ2FsaXRyb3MiKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KIyMjIENhbWJpb3MgZGUgbml2ZWwNCg0KQ2FwdHVyYW4gZWwgZWZlY3RvIGEgcGFydGlyIGRlIGNpZXJ0byBwZXJpb2RvLg0KDQpgYGB7cn0NCmNlcnZlemEgPC0gY2VydmV6YSAlPiUgDQogIG11dGF0ZShkMjAwMCA9IGlmX2Vsc2UoeWVhcihRdWFydGVyKT49MjAwMCwxLDApKQ0KY2VydmV6YQ0KYGBgDQoNCmBgYHtyfQ0KZml0X2JlZXIgPC0gY2VydmV6YSAlPiUgDQogIG1vZGVsKFRTTE0oQmVlciB+IHRyZW5kKCkgKyBzZWFzb24oKSArIGQyMDAwKSkNCg0KcmVwb3J0KGZpdF9iZWVyKQ0KYGBgDQoNCmBgYHtyfQ0KcCA8LSBhdWdtZW50KGZpdF9iZWVyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEJlZXIsIGNvbG9yID0gIkRhdG9zIikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3IgPSAiRml0dGVkIikpICsNCiAgbGFicyh4ID0gIkHDsW8iLCB5ID0gIk1lZ2FsaXRyb3MiKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmNlcnZlemEgJT4lIA0KICBnZ190c2Rpc3BsYXkoQmVlciAlPiUgZGlmZmVyZW5jZSg0KSwgcGxvdF90eXBlID0gInBhcnRpYWwiKQ0KYGBgDQoNCg0KDQoNCg0KDQpgYGB7cn0NCnRpYmJsZShtdWplciA9IGMoMSwwLDEsIDEpLA0KICAgICAgIGhvbWJyZSA9IGMoMCwxLDAsIDApLA0KICAgICAgIG5vbWJyZSA9IGMoIkFuZHJlYSIsIkp1YW4iLCJTb2bDrWEiLCJGZXIiKSkNCmBgYA0KDQoqKkN1YW5kbyBzZSBjcmVhbiB2YXJhaWJsZXMgZHVtbXksIGxhIGNhbnRpZGFkIGRlIGR1bW1pZXMgYSBnZW5lcmFyIGVzIHVuYSBtZW5vcyBxdWUgZWwgdG90YWwgZGUgY2F0ZWdvcsOtYXMqKi4gU2kgc29uIGRvcyBjYXRlZ29yw61hcywgc29sbyBuZWNlc2l0YW1vcyB1bmEgZHVtbXkuIFNpIHNvbiB0cmVzLCBuZWNlc2l0YW1vcyAyLCBldGMuDQoNCg0KIyMgUmVncmVzaW9uZXMgbm8gbGluZWFsZXMNCg0KDQojIyMgTW9kZWxvcyBleHBvbmVuY2lhbGVzDQoNCiogTW9kZWxvcyBsb2ctbG9nDQoNCiQkDQpcbG9ne3lfdH0gPSBcYmV0YV8wICsgXGJldGFfMSBcbG9ne3hfdH0NCiQkDQoNCkxhIGludGVycHJldGFjacOzbiBkZSBsb3MgY29lZmljaWVudGVzICgkXGJldGFfcyQpIGVzIGNvbW8gZWxhc3RpY2lkYWRlcyAoY2FtYmlvcyBwb3JjZW50dWFsZXMpLg0KDQoNCiogTW9kZWxvcyBsaW4tbG9nDQoNCiQkDQp5X3QgPSBcYmV0YV8wICsgXGJldGFfMSBcbG9ne3hfdH0NCiQkDQoNCiogTW9kZWxvcyBsb2ctbGluDQoNCiQkDQpcbG9ne3lfdH0gPSBcYmV0YV8wICsgXGJldGFfMSB4X3QNCiQkDQoNCiMjIyBNb2RlbG9zIGRlIHJlZ3Jlc2nDs24gbGluZWFsIHBvciBwYXJ0ZXMgKCpwaWVjZXdpc2UqKQ0KDQpBcXXDrSBsYSByZWdyZXNpw7NuIHNlIGNhbGN1bGEgcG9yIHBhcnRlcyBlbiBlbCB0aWVtcG8sIHBhcmEgY2FwdHVyYXIgZGlzdGludGFzIHRlbmRlbmNpYXMuDQoNCmBgYHtyfQ0KYm9zdG9uX21lbiA8LSBib3N0b25fbWFyYXRob24gJT4lIA0KICBmaWx0ZXIoRXZlbnQgPT0gIk1lbidzIG9wZW4gZGl2aXNpb24iKSAlPiUgDQogIG11dGF0ZShNaW51dGVzID0gYXMubnVtZXJpYyhUaW1lKS82MCkNCg0KcCA8LSBib3N0b25fbWVuICU+JSANCiAgYXV0b3Bsb3QoTWludXRlcykgKyANCiAgZ2d0aXRsZSgiVGllbXBvcyBnYW5hZG9yZXMgZGVsIG1hcmF0w7NuIGRlIEJvc3RvbiwgY2F0ZWdvcsOtYSBhYmllcnRhIGRlIGhvbWJyZXMiKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCmBgYHtyfQ0KZml0X2Jvc3RvbiA8LSBib3N0b25fbWVuICU+JSANCiAgbW9kZWwoDQogICAgbGluZWFsID0gVFNMTShNaW51dGVzIH4gdHJlbmQoKSksDQogICAgKQ0KDQpmY19ib3N0b24gPC0gZml0X2Jvc3RvbiAlPiUgZm9yZWNhc3QoaCA9IDEwKQ0KDQpib3N0b25fbWVuICU+JSANCiAgYXV0b3Bsb3QoTWludXRlcykgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSAuZml0dGVkLCBjb2xvciA9IC5tb2RlbCksIGRhdGEgPSBmaXR0ZWQoZml0X2Jvc3RvbikpICsNCiAgYXV0b2xheWVyKGZjX2Jvc3RvbiwgYWxwaGEgPSAwLjUsIGxldmVsID0gOTUpICsNCiAgZ2d0aXRsZSgiTWFyYXTDs24gZGUgQm9zdG9uLCBjYXQuIGFiaWVydGEgZGUgaG9tYnJlcyIpDQpgYGANCg0KDQpWYW1vcyBhIG1vZGVsYXIgbG9zIHRpZW1wb3MgY29uIHVuYSByZWdyZXNpw7NuIHBvciBwYXJ0ZXMuDQoNCmBgYHtyfQ0KZml0X2Jvc3RvbiA8LSBib3N0b25fbWVuICU+JSANCiAgbW9kZWwoDQogICAgbGluZWFsID0gVFNMTShNaW51dGVzIH4gdHJlbmQoKSksDQogICAgZXhwb25lbmNpYWwgPSBUU0xNKGxvZyhNaW51dGVzKSB+IHRyZW5kKCkpLA0KICAgIGBSZWcuIHBvciBwYXJ0ZXNgID0gVFNMTShNaW51dGVzIH4gdHJlbmQoa25vdHMgPSBjKDE5NDAsMTk4MCkpKQ0KICApDQoNCmZjX2Jvc3RvbiA8LSBmaXRfYm9zdG9uICU+JSBmb3JlY2FzdChoID0gMTApDQoNCmJvc3Rvbl9tZW4gJT4lIA0KICBhdXRvcGxvdChNaW51dGVzKSArDQogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG9yID0gLm1vZGVsKSwgZGF0YSA9IGZpdHRlZChmaXRfYm9zdG9uKSkgKw0KICBhdXRvbGF5ZXIoZmNfYm9zdG9uLCBhbHBoYSA9IDAuNSwgbGV2ZWwgPSA5NSkgKw0KICBnZ3RpdGxlKCJNYXJhdMOzbiBkZSBCb3N0b24sIGNhdC4gYWJpZXJ0YSBkZSBob21icmVzIikNCmBgYA0KDQpJbnRlbnRhbW9zIGFob3JhIHBvbmllbmRvIGxvcyBjYW1iaW9zIGVuIGxhIHRlbmRlbmNpYSBlbiBvdHJvcyBwZXJpb2RvczoNCg0KYGBge3J9DQpmaXRfYm9zdG9uIDwtIGJvc3Rvbl9tZW4gJT4lIA0KICBtb2RlbCgNCiAgICBsaW5lYWwgPSBUU0xNKE1pbnV0ZXMgfiB0cmVuZCgpKSwNCiAgICBleHBvbmVuY2lhbCA9IFRTTE0obG9nKE1pbnV0ZXMpIH4gdHJlbmQoKSksDQogICAgYFJlZy4gcG9yIHBhcnRlc2AgPSBUU0xNKE1pbnV0ZXMgfiB0cmVuZChrbm90cyA9IGMoMTk0MCwxOTgwKSkpLA0KICAgIGBSZWcuIHBvciBwYXJ0ZXMyYCA9IFRTTE0oTWludXRlcyB+IHRyZW5kKGtub3RzID0gYygxOTc1KSkpLA0KICAgIGBSZWcuIHBvciBwYXJ0ZXMzYCA9IFRTTE0oTWludXRlcyB+IHRyZW5kKGtub3RzID0gYygxOTE1LCAxOTUwLCAxOTg4KSkpLA0KICAgIGBSZWcuIHBvciBwYXJ0ZXM0YCA9IFRTTE0oTWludXRlcyB+IHRyZW5kKGtub3RzID0gYygxOTE1LCAxOTI3LCAxOTUwLCAxOTg1KSkpDQogICkNCg0KZmNfYm9zdG9uIDwtIGZpdF9ib3N0b24gJT4lIGZvcmVjYXN0KGggPSAxMCkNCg0KYm9zdG9uX21lbiAlPiUgDQogIGF1dG9wbG90KE1pbnV0ZXMpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3IgPSAubW9kZWwpLCBkYXRhID0gZml0dGVkKGZpdF9ib3N0b24pKSArDQogIGF1dG9sYXllcihmY19ib3N0b24sIGFscGhhID0gMC41LCBsZXZlbCA9IDk1KSArDQogIGdndGl0bGUoIk1hcmF0w7NuIGRlIEJvc3RvbiwgY2F0LiBhYmllcnRhIGRlIGhvbWJyZXMiKQ0KYGBgDQoNCmBgYHtyfQ0KYWNjdXJhY3koZml0X2Jvc3RvbikgJT4lIA0KICBhcnJhbmdlKE1BUEUpDQpgYGANCg0KDQpQcm9iYW1vcyB0cmFuc2Zvcm1hbmRvIGxhIHNlcmllIGNvbiBCb3gtQ294Og0KDQpgYGB7ciwgZmlnLmhlaWdodD05fQ0KbGFtYmRhIDwtIGJvc3Rvbl9tZW4gJT4lIA0KICBmZWF0dXJlcyhNaW51dGVzLCBndWVycmVybykgJT4lIA0KICBwdWxsKGxhbWJkYV9ndWVycmVybykNCg0KcDEgPC0gYm9zdG9uX21lbiAlPiUgDQogIGF1dG9wbG90KE1pbnV0ZXMpDQoNCnAyIDwtIGJvc3Rvbl9tZW4gJT4lIA0KICBhdXRvcGxvdChib3hfY294KE1pbnV0ZXMsIGxhbWJkYSA9IGxhbWJkYSkpDQoNCnAzIDwtIGJvc3Rvbl9tZW4gJT4lIA0KICBhdXRvcGxvdChsb2coTWludXRlcykpDQoNCnAxL3AyL3AzDQpgYGANCg0K